#include <QPainter>
#include <QBrush>
#include <QMouseEvent>

#include "gameinterface.h"
#include "../GameEngine/engine.h"
#include "../GameEngine/bullet.h"
#include "../GameEngine/map.h"

GameInterface::GameInterface()
{
    this->setFocusPolicy(Qt::ClickFocus);

    ViewX = ViewY = 0;
    ReferenceX = ReferenceY = 0;
    CenterView = true;
}

void GameInterface::NormalizeView()
{
    int MaxViewX = Engine::CurrentMap->GetBackground()->width() - width();
    int MaxViewY = Engine::CurrentMap->GetBackground()->height() - height();
    if(ViewX < 0) ViewX = 0;
    if(ViewX > MaxViewX) ViewX = MaxViewX;
    if(ViewY < 0) ViewY = 0;
    if(ViewY > MaxViewY) ViewY = MaxViewY;
}

void GameInterface::FocusOnMinion()
{
    ViewX = MyMinion->GetX() - width()/2;
    ViewY = MyMinion->GetY() - height()/2;
    NormalizeView();
}

void GameInterface::paintEvent(QPaintEvent *)
{
    if(CenterView) FocusOnMinion();

    QPainter painter(this);

    QVector<Minion *> VisibleMinions;
    QVector<Minion *> PartiallyVisible;
    GetVisibleMinions(VisibleMinions, PartiallyVisible);

    DrawBackground(painter, VisibleMinions);
    DrawMinions(painter, VisibleMinions, PartiallyVisible);
    DrawBullets(painter);
    DrawMessage(painter);
}

void GameInterface::GetVisibleMinions(QVector<Minion *> &VisibleMinions, QVector<Minion *> &PartiallyVisible)
{
    Minion *CurrentMinion = Engine::FirstMinion;
    while(CurrentMinion)
    {
        if(Engine::IsVisible(CurrentMinion, MyMinion->GetTeam()))
            VisibleMinions.append(CurrentMinion);
        else if(CurrentMinion->IsStructure())
            PartiallyVisible.append(CurrentMinion);

        CurrentMinion = CurrentMinion->GetNextMinion();
    }
}

void GameInterface::DrawBackground(QPainter &painter, QVector<Minion *> &VisibleMinions)
{
    // draw fog of war (set alpha of whole view to 0.5)
    painter.setCompositionMode(QPainter::CompositionMode_Source);
    painter.fillRect(0, 0, width(), height(), QColor(0,0,0,128));

    // draw visible range of all minions (reset alpha in range to 1.0)
    painter.setBrush(QBrush(QColor(0,0,0,255)));
    foreach(Minion *CurrentMinion, VisibleMinions)
    {
        int Sight;
        if(CurrentMinion->GetTeam() == MyMinion->GetTeam() && !CurrentMinion->IsDead())
        {
            Sight = CurrentMinion->GetSight() + CurrentMinion->GetSize();
        }
        else // dead or enemy (at the edge of visibility must still have visibility circle drawn)
        {
            Sight = CurrentMinion->GetSize() + 5;
        }

        int X = CurrentMinion->GetX() - ViewX - Sight;
        int Y = CurrentMinion->GetY() - ViewY - Sight;
        painter.drawEllipse(X, Y, Sight*2, Sight*2);

        CurrentMinion = CurrentMinion->GetNextMinion();
    }

    // draw background on top of visibility map
    painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
    painter.drawPixmap(0, 0, width(), height(), *(Engine::CurrentMap->GetBackground()),
                       ViewX, ViewY, width(), height());
    painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    painter.setBrush(Qt::NoBrush);
}

void GameInterface::DrawMinions(QPainter &painter, QVector<Minion *> &VisibleMinions,
                                QVector<Minion *> &PartiallyVisible)
{
    DrawMinionsSprites(painter, VisibleMinions);
    DrawPartiallyVisibleSprites(painter, PartiallyVisible);
    DrawMinionsInfoBars(painter, VisibleMinions);
}

void GameInterface::DrawMinionsSprites(QPainter &painter, QVector<Minion*> &VisibleMinions)
{
    foreach(Minion *CurrentMinion, VisibleMinions)
    {
        double X = CurrentMinion->GetX() - ViewX;
        double Y = CurrentMinion->GetY() - ViewY;
        double O = CurrentMinion->GetOrientation();
        double TO = CurrentMinion->GetTurretOrientation();

        if(CurrentMinion->IsDead())
        {
            if(!CurrentMinion->IsWreck())
            {
                double scale = 0.8;
                int TimeToRespawn = CurrentMinion->GetTimeToRespawn();
                if(TimeToRespawn < 0) scale = 1.0 - 0.2 * (Engine::TicksPerSecond + TimeToRespawn) / Engine::TicksPerSecond;
                painter.scale(scale, scale);
                X *= 1.0 / scale;
                Y *= 1.0 / scale;
                painter.setCompositionMode(QPainter::CompositionMode_Darken);
            }
        }

        painter.translate(X, Y);
        if(!CurrentMinion->IsWreck())
        {
            DrawMinionMarkers(painter, CurrentMinion);
        }

        painter.rotate(O*360);
        DrawMinionBase(painter, CurrentMinion);

        if(!CurrentMinion->IsWreck())
        {
            painter.rotate((TO - O)*360);
            DrawMinionTurret(painter, CurrentMinion);
        }

        painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
        painter.resetTransform();
    }
}

void GameInterface::DrawPartiallyVisibleSprites(QPainter &painter, QVector<Minion*> &PartiallyVisible)
{
    foreach(Minion *CurrentMinion, PartiallyVisible)
    {
        double X = CurrentMinion->GetX() - ViewX;
        double Y = CurrentMinion->GetY() - ViewY;
        double O = Engine::CurrentMap->GetTeamDefaultOrientation(CurrentMinion->GetTeam());

        painter.translate(X, Y);
        painter.rotate(O*360);
        DrawMinionBase(painter, CurrentMinion);

        painter.resetTransform();
    }
}

void GameInterface::DrawMinionsInfoBars(QPainter &painter, QVector<Minion*> &VisibleMinions)
{
    painter.setPen(Qt::NoPen);
    foreach(Minion *CurrentMinion, VisibleMinions)
    {
        if(!CurrentMinion->IsDead())
        {
            double X = CurrentMinion->GetX() - ViewX;
            double Y = CurrentMinion->GetY() - ViewY;
            int hp = CurrentMinion->GetHealth();
            int maxhp = CurrentMinion->GetMaxHealth();
            int length = CurrentMinion->GetSize();

            if(CurrentMinion->IsPlayer())
            {
                painter.setPen(Qt::black);
                painter.drawText(X - 40, Y - length - 22, 80, 20, Qt::AlignCenter, QString("%1, %2").
                                 arg(CurrentMinion->GetPlayerName()).
                                 arg(CurrentMinion->GetLevel()));
                painter.setPen(Qt::NoPen);
            }

            painter.setBrush(QBrush(Qt::red));
            painter.drawRect(X - length, Y - length - 5, 2 * length, 5);
            painter.setBrush(QBrush(Qt::green));
            painter.drawRect(X - length, Y - length - 5, 2 * length * hp / maxhp, 5);
        }
    }
    painter.setBrush(Qt::NoBrush);
    painter.setPen(Qt::black);
}

void GameInterface::DrawBullets(QPainter &painter)
{
    Bullet *CurrentBullet = Engine::FirstBullet;
    while(CurrentBullet)
    {
        int X = CurrentBullet->GetX() - ViewX;
        int Y = CurrentBullet->GetY() - ViewY;
        painter.drawEllipse(X, Y, 5, 5);
        CurrentBullet = CurrentBullet->GetNextBullet();
    }
}

void GameInterface::DrawMinionMarkers(QPainter &painter, Minion *CurrentMinion)
{
    if(CurrentMinion == MyMinion || CurrentMinion == MyMinion->GetCurrentTarget())
    {
        QPen Pen;
        Pen.setWidth(3);
        int size = CurrentMinion->GetSize();
        Pen.setColor(CurrentMinion == MyMinion ? Qt::yellow : Qt::red);
        painter.setPen(Pen);
        painter.drawEllipse(-size-2, -size-2, size * 2 + 4, size * 2 + 4);
        painter.setPen(Qt::NoPen);
    }
}

void GameInterface::DrawMinionBase(QPainter &painter, Minion *CurrentMinion)
{
    QPixmap *minion = CurrentMinion->GetBaseSprite();
    if(minion != nullptr)
    {
        painter.drawPixmap(-minion->width()/2, -minion->height()/2,
                       minion->width(), minion->height(), *minion);
    }
}

void GameInterface::DrawMinionTurret(QPainter &painter, Minion *CurrentMinion)
{
    QPixmap *turret = CurrentMinion->GetTurretSprite();
    if(turret != nullptr)
    {
        painter.drawPixmap(-turret->width()/2, -turret->height()/2,
                       turret->width(), turret->height(), *turret);
    }
}

void GameInterface::DrawMessage(QPainter &painter)
{
    if(MyMinion->IsDead())
    {
        painter.setFont(QFont("Arial", 15, QFont::Bold));
        painter.setPen(QColor(Qt::yellow));
        int TimeToRespawn = MyMinion->GetTimeToRespawn();
        if(!MyMinion->IsWreck() )
        {
            if(TimeToRespawn > 0 && !MyMinion->IsRespawning())
            {
                painter.drawText(10, height() - 30, QString("You are dead. You will respawn in %1 seconds.").
                                 arg(TimeToRespawn / Engine::TicksPerSecond + 1));
            }
            else
            {
                painter.drawText(10, height() - 30,
                                 "You can't respawn here. Move away to a free spot.");
            }
        }
    }
}

void GameInterface::mousePressEvent(QMouseEvent *e)
{
    if(e->button() == Qt::LeftButton)
    {
        NewOrder(e->x() + ViewX, e->y() + ViewY);
    }
    else if(e->button() == Qt::RightButton && !CenterView)
    {
        ReferenceX = e->localPos().x();
        ReferenceY = e->localPos().y();
    }
}

void GameInterface::mouseMoveEvent(QMouseEvent *e)
{
    if((e->buttons() & Qt::RightButton) && !CenterView)
    {
        ViewX += ReferenceX - e->localPos().x();
        ReferenceX = e->localPos().x();
        ViewY += ReferenceY - e->localPos().y();
        ReferenceY = e->localPos().y();

        NormalizeView();
    }
}

void GameInterface::keyPressEvent(QKeyEvent *e)
{
    if(e->key() == Qt::Key_C) CenterView = !CenterView;
}
